#!/usr/bin/env python
# -*- coding: utf-8 -*-

import threading
import zmq
import zmq.asyncio
import threading
from typing import Dict, Optional, List
from flask import Flask, session
from flask_socketio import SocketIO
from flask_cors import CORS
from common.config import ServiceConfig, SecurityConfig
from common.utils import LogUtil
from web_server.models.service_info import ServiceInfo
from web_server.models.device_client import DeviceClient
from common.webrtc_errno import WebRTCErrno

logger = LogUtil.get_logger(__name__)

class DeviceProxy:
    """设备代理类"""

    def __init__(self):
        self.app = None
        self.session = None
        self.socketio = None
        self.__device_clients_map: Dict[str, DeviceClient] = {}
        self.__device_service_map: Dict[str, str] = {}
        self.__lock = threading.Lock()
        self.__reload_cb = None
        self.__zmq_syn_context = None
        self.__zmq_aio_context = None


        self.app = self.__init_flask_app()
        self.session = session
        self.socketio = SocketIO(self.app, cors_allowed_origins="*", async_mode='eventlet')
        self.__zmq_syn_context = zmq.Context()
        self.__zmq_aio_context = zmq.asyncio.Context()

    def __del__(self):
        self.destroy()

    def destroy(self):
        """析构函数，清理资源并主动断开所有socketio连接"""
        if self.socketio:
            try:
                # 主动断开所有客户端连接
                logger.info("Disconnecting all SocketIO clients...")
                # self.socketio.disconnect() is not a valid method
                # Instead, we can iterate over the connected clients and disconnect them individually
                for sid, client in self.socketio.server.eio.sockets.items():
                    self.socketio.disconnect(sid)
                logger.info("All SocketIO clients disconnected successfully")
            except Exception as e:
                logger.error(f"Error disconnecting SocketIO clients: {e}")
            finally:
                self.socketio = None
        self.session = None
        self.app = None
        with self.__lock:
            for client in self.__device_clients_map:
                client.destroy()
            self.__device_clients_map.clear()
            self.__device_service_map.clear()
        self.__zmq_syn_context.term()
        self.__zmq_aio_context.term()

    def __init_flask_app(self):
        """初始化Flask应用

        Returns:
            Flask: Flask应用实例
        """
        # 创建Flask应用
        app = Flask(__name__, static_url_path='/', static_folder='static')
        # 启用CORS
        CORS(app)
        # 配置app
        app.logger = logger
        app.config['HOST'] = ServiceConfig.HTTP_HOST
        app.config['PORT'] = ServiceConfig.HTTP_PORT
        app.config['DEBUG'] = ServiceConfig.HTTP_DEBUG
        app.config['USE_RELOADER'] = ServiceConfig.HTTP_USE_RELOADER
        app.config['SECRET_KEY'] = SecurityConfig.SECRET_KEY
        return app
    
    def set_reload_cb(self, cb: callable):
        self.__reload_cb = cb
    
    def handle_del_services(self, service_info: ServiceInfo):
        """线程安全地移除服务
        
        Args:
            service_info (ServiceInfo): 服务信息
        """
        logger.debug(f"移除服务: {service_info.id}")
        with self.__lock:
            client = self.__device_clients_map.pop(service_info.id, None)
            if client is None:
                return
            for sn in client.get_device_info_map().keys():
                self.__device_service_map.pop(sn, None)
            client.destroy()
            # 设备服务客户端设备列表有了更新，通知消息服务
            if self.__reload_cb:
                self.__reload_cb(service_info.id, 'reload_devices')
            else:
                logger.warning(f"未注册消息通知回调")

    def handle_old_services(self, service_info: ServiceInfo):
        """线程安全地处理已存在的服务
        
        Args:
            service_info (ServiceInfo): 服务信息
        """
        # logger.debug(f"检查服务: {service_info.id}")
        with self.__lock:
            client = self.__device_clients_map.get(service_info.id, None)
            if client is None:
                return
            current_device_info_map = client.get_device_info_map()
            if client.update_device_info_map() is True:
                # 设备服务客户端设备列表有了更新，同步更新设备-->服务映射表
                update_device_info_map = client.get_device_info_map()
                for sn in current_device_info_map.keys():
                    if update_device_info_map.get(sn, None) is None:
                        self.__device_service_map.pop(sn, None)
                for sn in update_device_info_map.keys():
                    self.__device_service_map[sn] = service_info.id
                # 设备服务客户端设备列表有了更新，通知消息服务
                if self.__reload_cb:
                    self.__reload_cb(service_info.id, 'reload_devices')
                else:
                    logger.warning(f"未注册消息通知回调")

    def handle_new_services(self, service_info: ServiceInfo):
        """线程安全地处理新服务
        
        Args:
            service_info (ServiceInfo): 服务信息
        """
        is_new_service = True
        expired_service_ids = []
        with self.__lock:
            for client in self.__device_clients_map.values():
                if (client.service_info.host == service_info.host and
                    client.service_info.rep_port == service_info.rep_port and
                    client.service_info.pub_port == service_info.pub_port and
                    client.service_info.res_port == service_info.res_port and
                    client.service_info.aio_port == service_info.aio_port):
                    if client.service_info.timestamp < service_info.timestamp:
                        expired_service_ids.append(client.service_info.id)
                    else:
                        is_new_service = False
            for expired_service_id in expired_service_ids:
                self.__device_clients_map.pop(expired_service_id, None)
            if is_new_service:
                logger.debug(f"连接服务: {service_info.id}")
                client = DeviceClient(service_info, self.__zmq_syn_context, self.__zmq_aio_context)
                self.__device_clients_map[service_info.id] = client
                if client.update_device_info_map(force_reload=True) is True:
                    # 设备服务客户端设备列表有了更新，同步更新设备-->服务映射表
                    for sn in client.get_device_info_map().keys():
                        logger.debug(f"new service {service_info.id} adds device {sn}")
                        self.__device_service_map[sn] = service_info.id
                    # 设备服务客户端设备列表有了更新，通知消息服务
                    if self.__reload_cb:
                        self.__reload_cb(service_info.id, 'reload_devices')
                    else:
                        logger.warning(f"未注册消息通知回调")

    def load_devices(self, force_reload=False):
        """加载所有设备信息

        Args:
            force_reload (bool, optional): 是否强制重新加载. Defaults to False.
        """
        device_info_list = []
        with self.__lock:
            for client in self.__device_clients_map.values():
                client.update_device_info_map(force_reload=force_reload)
                device_info_list.extend(list(client.get_device_info_map().values()))
        return device_info_list

    async def handle_webrtc_offer(self, data: dict):
        """处理WebRTC Offer

        Args:
            data (dict): webrtc offer信令数据

        Returns:
            dict: 设备服务器响应
        """
        with self.__lock:
            service_id = self.__device_service_map.get(data['device_sn'], None)
            if service_id is None:
                logger.error(f"failed to handle webrtc offer: device service not found")
                return {
                    'type': 'webrtc_error',
                    'data': {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_offer',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc offer'
                    }
                }
            client = self.__device_clients_map.get(service_id, None)
            if client is None:
                logger.error(f"failed to handle webrtc offer: device client not found")
                return {
                    'type': 'webrtc_error',
                    'data': {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_offer',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc offer'
                    }
                }
            return await client.handle_webrtc_offer(data)


    async def handle_webrtc_candidate(self, data: dict):
        """处理WebRTC候选

        Args:
            data (dict): webrtc candidate信令数据

        Returns:
            dict: 设备服务器响应
        """
        with self.__lock:
            service_id = self.__device_service_map.get(data['device_sn'], None)
            if service_id is None:
                return {
                    'type': 'webrtc_error',
                    'data': {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_candidate',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc candidate'
                    }
                }
            client = self.__device_clients_map.get(service_id, None)
            if client is None:
                return {
                    'type': 'webrtc_error',
                    'data': {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_candidate',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc candidate'
                    }
                }
            return await client.handle_webrtc_candidate(data)

    async def handle_webrtc_disconnect(self, data: dict):
        """处理WebRTC断开连接

        Args:
            data (dict): webrtc disconnect信令数据

        Returns:
            dict: 处理结果
        """
        with self.__lock:
            service_id = self.__device_service_map.get(data['device_sn'], None)
            if service_id is None:
                return {
                    'type': 'webrtc_error',
                    'data': {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_disconnect',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc disconnect'
                    }
                }
            client = self.__device_clients_map.get(service_id, None)
            if client is None:
                return {
                    'type': 'webrtc_error',
                    'data': {
                        'signal_syn_seq': data['signal_ack_seq'],
                        'signal_ack_seq': data['signal_syn_seq'] + 1,
                        'client_id': data['client_id'],
                        'device_sn': data['device_sn'],
                        'source': 'webrtc_disconnect',
                        'errnum': WebRTCErrno.ESIGSVR.value,
                        'errmsg': 'Failed to forward webrtc disconnect'
                    }
                }
            return await client.handle_webrtc_disconnect(data)

    def capture_screen(self, sn: str, filename: str):
        """捕获屏幕

        Args:
            sn (str): 设备SN
            filename (str): 文件名

        Returns:
            str: 图片URL
        """
        with self.__lock:
            service_id = self.__device_service_map.get(sn, None)
            if service_id is None:
                return None
            client = self.__device_clients_map.get(service_id, None)
            if client is None:
                return None
            return client.capture_screen(sn, filename)

